package com.da.javalog.aop;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.da.javalog.common.EnableLog;
import com.da.javalog.common.EnableOtherLog;
import com.da.javalog.common.EnableQueryLog;
import com.da.javalog.utils.CommonUtil;
import com.da.javalog.utils.JsonUtils;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

/**
 * @author chenlida
 * @date 2020/4/12 13:56
 *
 * LoggerFactory.getLogger("AOP");这里获取的都是logback.xml里面配置好的
 */
@Aspect
@Component
@Slf4j
public class AOPClient {
    private static Map<String, Logger> loggerMap;

    /**
     * 注册logger
     */
    static {
        loggerMap = new HashMap<>();
        Logger defaultLog = LoggerFactory.getLogger(AOPClient.class);
        Logger aopLog = LoggerFactory.getLogger("AOP");
        Logger bizLog = LoggerFactory.getLogger("BIZ");
        Logger testLog = LoggerFactory.getLogger("TEST_MSG");

        loggerMap.put("DEFAULT", defaultLog);
        loggerMap.put("AOP", aopLog);
        loggerMap.put("BIZ", bizLog);
        loggerMap.put("TEST_MSG", testLog);
    }

    /**
     * aop不拦截。主要是为了防止aop拦截
     */
    private static final List<String> aopFilterList = Lists.newArrayList("xxService", "xxxService");

    @Pointcut("execution(* com.da.javalog.handler..*.*(..))")
    public void bizServiceA() {

    }

    @Pointcut("execution(* com.da.javalog.service..*.*(..))")
    public void bizServiceB() {

    }

    @Around(value = "bizServiceA() || bizServiceB()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        Object result = null;
        long current = System.currentTimeMillis();
        // 这里可以存一些链路追踪的信息
        MDC.put("JOCKER_DA", CommonUtil.getUuid());
        try {
            result = pjp.proceed();
        } catch (Throwable e) {
            log.error("AOP_ERROR", e);
            throw e;
        } finally {
            print(pjp, result, current);
        }

        return result;
    }

    /**
     * 打印相关日志
     *
     * @param pjp
     */
    private void print(ProceedingJoinPoint pjp, Object result, long current) {
        try {
            printDetailLog(pjp, result, current);
        } catch (Exception e) {
            log.error("DETAILLOG_PRINT_ERROR", e);
        }
    }

    /**
     * 打印日志的出入参详情
     */
    private void printDetailLog(ProceedingJoinPoint pjp, Object result, long current) {
        Method targetMethod = ((MethodSignature)pjp.getSignature()).getMethod();
        String simpleClassName = targetMethod.getDeclaringClass().getSimpleName();
        String methodName = simpleClassName + "." + targetMethod.getName();
        long costTime = System.currentTimeMillis() - current;
        Object service = pjp.getSignature().getDeclaringType().getAnnotation(Service.class);

        // 不过滤会因循环依赖导致溢出报错
        if (aopFilterList.contains(simpleClassName)) {
            return;
        }

        // service的出入参数打印在默认的日志文件里面 查询相关的类
        if (service != null && enableQueryLog(pjp)) {
            List<String> list = getQueryParams(pjp.getArgs(), result);
            loggerMap.get("DEFAULT").info("Client_Request Method = {}, Args = {}, Result = {}, CostTime = {} ms",
                new Object[] {methodName, list.get(0), list.get(1), costTime});
            return;
        }

        // service的出入参数打印在默认的日志文件里面
        if (service != null && enableLog(pjp)) {
            List<String> list = getParams(pjp.getArgs(), result);
            loggerMap.get("DEFAULT").info("Client_Request Method = {}, Args = {}, Result = {}, CostTime = {} ms",
                new Object[] {methodName, list.get(0), list.get(1), costTime});
            return;
        }

        // 不是service，且enableLog不为空的打印在aop日志里面
        if (service == null && enableLog(pjp)) {
            List<String> list = getParams(pjp.getArgs(), result);
            loggerMap.get("AOP").info("Aop_Request Method = {}, Args = {}, Result = {}, CostTime = {} ms",
                new Object[] {methodName, list.get(0), list.get(1), costTime});
            return;
        }

        // 不是service，其他日志
        if (service == null && enableOtherLog(pjp)) {
            Object otherLog = pjp.getSignature().getDeclaringType().getAnnotation(EnableOtherLog.class);
            EnableOtherLog j = (EnableOtherLog)otherLog;
            //Logger logger = LoggerFactory.getLogger(j.topic());
            Logger logger = loggerMap.get(j.topic());
            List<String> list = getParams(pjp.getArgs(), result);
            logger.info("Other_Request Method = {}, Args = {}, Result = {}, CostTime = {} ms",
                new Object[] {methodName, list.get(0), list.get(1), costTime});
            return;
        }
    }

    private boolean enableLog(ProceedingJoinPoint pjp) {
        Method targetMethod = ((MethodSignature)pjp.getSignature()).getMethod();
        Object enableLog2Class = pjp.getSignature().getDeclaringType().getAnnotation(EnableLog.class);
        Object enableLog2Method = targetMethod.getAnnotation(EnableLog.class);
        return enableLog2Class != null || enableLog2Method != null;
    }

    private boolean enableQueryLog(ProceedingJoinPoint pjp) {
        Method targetMethod = ((MethodSignature)pjp.getSignature()).getMethod();
        Object enableLog2Class = pjp.getSignature().getDeclaringType().getAnnotation(EnableQueryLog.class);
        Object enableLog2Method = targetMethod.getAnnotation(EnableQueryLog.class);
        return enableLog2Class != null || enableLog2Method != null;
    }

    private boolean enableOtherLog(ProceedingJoinPoint pjp) {
        Method targetMethod = ((MethodSignature)pjp.getSignature()).getMethod();
        Object enableLog2Class = pjp.getSignature().getDeclaringType().getAnnotation(EnableOtherLog.class);
        Object enableLog2Method = targetMethod.getAnnotation(EnableOtherLog.class);
        return enableLog2Class != null || enableLog2Method != null;
    }

    private List<String> getParams(Object[] args, Object result) {
        /**
         * bundle的出入参数打印在默认的日志文件里面
         */
        String argsStr = JsonUtils.serializeNoException(args);
        String resultStr = JsonUtils.serializeNoException(result);
        //防止日志过长，进行截断处理
        //        if (StringUtils.length(argsStr) > 2000) {
        //            argsStr = argsStr.substring(0, 1990) + "...";
        //        }
        //        if (StringUtils.length(resultStr) > 2000) {
        //            resultStr = resultStr.substring(0, 1990) + "...";
        //        }
        List<String> list = Lists.newArrayList();
        list.add(argsStr);
        list.add(resultStr);
        return list;
    }

    private List<String> getQueryParams(Object[] args, Object result) {
        /**
         * bundle的出入参数打印在默认的日志文件里面 application.log
         * 这里不要直接对result进行操作，不然会影响返回值
         */
        String argsStr = JsonUtils.serializeNoException(args);
        String resultStr = JsonUtils.serializeNoException(result);
        //String resultStr;
        //if (result instanceof SrpPageResult) {
        //    SrpPageResult r = (SrpPageResult)result;
        //    SrpPageResult srpPageResult = new SrpPageResult();
        //    srpPageResult.setPageSize(r.getPageSize());
        //    srpPageResult.setPageIndex(r.getPageIndex());
        //    srpPageResult.setTotalCount(r.getTotalCount());
        //    srpPageResult.setErrorCode(r.getErrorCode());
        //    srpPageResult.setErrorMessage(r.getErrorMessage());
        //    srpPageResult.setSuccess(r.isSuccess());
        //    resultStr = JsonUtils.serializeNoException(srpPageResult);
        //} else if (result instanceof SrpResult) {
        //    SrpResult r = (SrpResult)result;
        //    SrpResult srpResult = new SrpResult();
        //    srpResult.setErrorCode(r.getErrorCode());
        //    srpResult.setErrorMessage(r.getErrorMessage());
        //    srpResult.setSuccess(r.isSuccess());
        //    resultStr = JsonUtils.serializeNoException(srpResult);
        //} else {
        //    resultStr = JsonUtils.serializeNoException(result);
        //}
        List<String> list = Lists.newArrayList();
        list.add(argsStr);
        list.add(resultStr);
        return list;
    }
}
